import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { Component, Input, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MultiAutocompleteModule } from './multi-autocomplete.module';
import { MultiAutocompleteComponent } from './multi-autocomplete.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

@Component({
  template: `
    <lv-multi-autocomplete
      #multi
      [ngModel]="selectedValue"
      [lvData]="lvData"
      [lvPlaceholder]="lvPlaceholder"
      [lvPosition]="lvPosition"
      [lvLoading]="lvLoading"
      [lvAllowNewItem]="lvAllowNewItem"
      [lvShowNoData]="lvShowNoData"
      [lvMaxHeight]="lvMaxHeight"
      [lvPanelWidth]="lvPanelWidth"
      (lvInputChange)="inputChange($event)"
    ></lv-multi-autocomplete>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestMultiAutocompleteComponent {
  @ViewChild('multi', { static: true }) multiComponent: MultiAutocompleteComponent;
  selectedValue: string[];
  lvData: string[] = ['test1', 'test2'];
  @Input() lvPlaceholder: string;
  @Input() lvPosition = 'bottomLeft';
  @Input() lvLoading: boolean;
  @Input() lvAllowNewItem: boolean;
  @Input() lvShowNoData: boolean = true;
  @Input() lvMaxHeight: string | number = '2em';
  @Input() lvPanelWidth: string | number;
  inputChange() {}
}

describe('MultiAutocompleteComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestMultiAutocompleteComponent],
      imports: [MultiAutocompleteModule, FormsModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));

  describe('render', () => {
    let component: TestMultiAutocompleteComponent;
    let fixture: ComponentFixture<TestMultiAutocompleteComponent>;
    let cdr: ChangeDetectorRef;
    const renderComp = fakeAsync(para => {
      for (const [key, value] of Object.entries(para)) {
        component[key] = value;
      }
      cdr.detectChanges();
      tick(500);
      cdr.detectChanges();
      tick(500);

      const input = fixture.nativeElement.querySelector('.lv-input');

      input.focus();
      cdr.detectChanges();
      tick(500);

      input.value = 'test';
      input.dispatchEvent(new Event('input'));
      cdr.detectChanges();
      tick(500);
      cdr.detectChanges();
      tick(500);
      expect(document.body).toMatchSnapshot();
    });

    beforeEach(() => {
      fixture = TestBed.createComponent(TestMultiAutocompleteComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
    });

    // 不同弹出位置渲染
    it('with lvPlaceholder and lvPosition', () => {
      renderComp({ lvPlaceholder: 'input', lvPosition: 'topLeft' });
    });

    // 空面板渲染
    it('with lvShowNoData', () => {
      renderComp({ lvData: [], lvShowNoData: true });
      renderComp({ lvData: [], lvShowNoData: false });
    });

    // 加载中渲染
    it('with lvLoading', () => {
      renderComp({ lvLoading: true });
    });

    // 不同组件高度和下拉面板宽度渲染
    it('with lvMaxHeight and lvPanelWidth', () => {
      renderComp({ lvMaxHeight: '10px', lvPanelWidth: '10px' });
    });
  });

  describe('work', () => {
    let component: TestMultiAutocompleteComponent;
    let fixture: ComponentFixture<TestMultiAutocompleteComponent>;

    beforeEach(() => {
      fixture = TestBed.createComponent(TestMultiAutocompleteComponent);
      component = fixture.componentInstance;
      jest.spyOn(component, 'inputChange');
      fixture.autoDetectChanges();
    });

    // 正常输入并选中，能触发inputChange，然后能正常删除
    it('when input', fakeAsync(() => {
      // 输入后出来选择面板
      const input = fixture.nativeElement.querySelector('.lv-input');

      input.focus();
      fixture.detectChanges();
      tick(500);

      input.value = 'test';
      input.dispatchEvent(new Event('input'));
      tick(500);
      expect(component.inputChange).toBeCalled();

      const lint = fixture.nativeElement.querySelector('.lv-multi-autocomplete-lint');
      expect(lint.textContent).toContain('test');

      // 联想选择后生成tag标签
      const item = document.querySelector('.lv-autocomplete-item');
      item.dispatchEvent(new Event('click'));
      tick(500);
      const tag = fixture.nativeElement.querySelector('.lv-tag-item');
      expect(tag.textContent).toContain('test1');

      // tag标签能正常删除
      const tagRemove = fixture.nativeElement.querySelector('.lv-tag-remove');
      tagRemove.dispatchEvent(new Event('click'));
      tick(500);
      expect(fixture.nativeElement.querySelector('.lv-tag-item')).toBeNull();
    }));

    // lvAllowNewItem传入true时，支持直接回车输入生效
    it('with lvAllowNewItem', fakeAsync(() => {
      component.lvAllowNewItem = true;
      fixture.detectChanges();

      const input = fixture.nativeElement.querySelector('.lv-input');
      input.value = 'test';
      input.dispatchEvent(new Event('input'));
      tick(500);

      const e = new KeyboardEvent('keydown', {
        keyCode: 13,
        bubbles: true,
      });
      input.dispatchEvent(e);
      tick(500);
      const tag = fixture.nativeElement.querySelector('.lv-tag-item');
      expect(tag.textContent).toContain('test');
    }));
  });
});
